import { Injectable, Logger } from '@nestjs/common';
import { ChildProcess, exec, spawn } from 'child_process';
import { readFileSync } from 'fs';
import { platform } from 'os';
import {
  awaitTime,
  executeContinuously,
  PerformanceTimer,
} from 'src/utils/time';
// 使用 Map 管理所有进程
const processes = new Map<string, ChildProcess>();
let mitmProcess: ChildProcess | null = null;
type commandOptions =
  | {
      commandStr: string;
      command?: string;
      args?: string[];
      notWait?: boolean;
      onError?: (msg: string) => Promise<void> | void;
      key: string; // 进程标识
    }
  | {
      command: string;
      args: string[];
      commandStr?: string;
      notWait?: boolean;
      onError?: (msg: string) => Promise<void> | void;
      key: string; // 进程标识
    };
/**
 * 脚本服务类，负责管理各种脚本进程的执行、重启和清理工作
 */
@Injectable()
export class ScriptService {
  logger = new Logger(ScriptService.name);
  private resetProcess: ChildProcess | null = null;
  private lotterySignProcess: ChildProcess | null = null;
  private pdfLotterySignProcess: ChildProcess | null = null;
  private lotterySignProcessNotClose: ChildProcess | null = null;
  private closeProcess: ChildProcess | null = null;
  private readonly path: string = `${process.cwd()}/src/scripts`;
  constructor() {
    this.setupExitHandlers();
  }
  /**
   * 运行脚本
   * @param param0
   * @returns
   */
  async run(options: commandOptions) {
    const { commandStr, command, args, notWait, onError, key } = options;
    const { result, duration } = await PerformanceTimer.time(async () => {
      const prom = new Promise((resolve, reject) => {
        // 使用 spawn 替代 exec 以获得更好的控制
        const [useCommand, ...useArgs] = commandStr
          ? commandStr.split(' ')
          : [command || '', ...(args || [])];
        this.logger.log(`执行脚本 ${useCommand} ${useArgs.join(' ')}`);

        const child = spawn(useCommand, useArgs, {
          detached: true, // 允许创建独立的进程组
          stdio: 'ignore',
        });

        // 存储进程引用
        if (key) {
          processes.set(key, child);
        }

        child.on('error', (error) => {
          this.logger.error(`${commandStr} 脚本执行失败 ${error.message}`);
          if (onError) void onError(error.message);
          reject(error);
        });

        child.on('exit', (code, signal) => {
          if (code === 0) {
            this.logger.log(`${commandStr} 脚本执行成功`);
            resolve(child);
          } else {
            const errorMsg = `脚本退出，代码: ${code}, 信号: ${signal}`;
            this.logger.error(`${commandStr} ${errorMsg}`);
            if (onError) void onError(errorMsg);
            reject(new Error(errorMsg));
          }
        });

        if (notWait) {
          resolve(child);
        }
      });
      return prom as Promise<ChildProcess>;
    });
    return { result, duration };
  }
  /**
   * 获取脚本位置
   * 默认 src/scripts
   */
  getScriptPath(name: string) {
    return `${this.path}/${name}`;
  }
  public async start(): Promise<void> {
    await this.restartMitm();
    this.setupExitHandlers();
  }
  public async restartMitm(
    count = 0,
    max = 3,
  ): Promise<{ result: ChildProcess; duration: number }> {
    this.logger.log('[mitmweb] 正在终止进程');

    // 使用改进的进程终止方法
    if (mitmProcess) {
      await this.killProcessTree(mitmProcess);
      mitmProcess = null;
    }

    await awaitTime(2000); // 等待更长时间确保进程终止

    try {
      const configPath = this.getScriptPath('mitmproxy.config.json');
      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
      const config = JSON.parse(readFileSync(configPath, 'utf-8'));
      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
      const baseUrl = config.BAST_URL;
      const command = 'mitmweb';
      const args = [
        '-p',
        '9000',
        '-w',
        this.getScriptPath('traffic.mitm'),
        '-s',
        this.getScriptPath('redirect_script.py'),
        '--set',
        `view_filter="~d ${baseUrl}"`,
      ];
      const { result, duration } = await this.run({
        command,
        args,
        notWait: true,
        key: 'mitmweb', // 添加进程标识
      });

      mitmProcess = result;
      this.logger.log('[mitmweb] 进程已启动');
      await awaitTime(5000);
      await this.closeMitmBrowser();
      return { result, duration };
    } catch (error) {
      count++;
      if (count >= max) {
        this.logger.error(`mitmweb 重新启动失败 ${(error as Error).message}`);
        if (mitmProcess) {
          await this.killProcessTree(mitmProcess);
          mitmProcess = null;
        }
        throw error;
      }
      return this.restartMitm(count, max);
    }
  }

  // 重启小程序
  async restartMiniApp() {
    await this.killProcessTree(this.resetProcess);
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('restart_mini.exe'),
      key: 'restart_mini', // 添加进程标识
    });
    this.resetProcess = result;
    return { duration, result };
  }
  // 重启网关，重启小程序
  async runReset() {
    const { duration } = await this.restartMitm();
    const { duration: duration2 } = await this.restartMiniApp();
    return { duration: duration + duration2 };
  }
  // 抽奖任务进行签到
  async lotterySign() {
    await this.killProcessTree(this.lotterySignProcess);
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('lottery_sign.exe'),
      key: 'lottery_sign', // 添加进程标识
    });
    this.lotterySignProcess = result;
    return { duration, result };
  }
  // pdf小程序签到
  async pdfLotterSign() {
    await this.killProcessTree(this.pdfLotterySignProcess);
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('pdf_lottery_sign.exe'),
      key: 'pdf_lottery_sign', // 添加进程标识
    });
    this.pdfLotterySignProcess = result;
    return { duration, result };
  }
  async lotterySignNotClose() {
    await this.killProcessTree(this.lotterySignProcessNotClose);
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('lottery_sign_not_close.exe'),
      key: 'lottery_sign_not_close', // 添加进程标识
    });
    this.lotterySignProcessNotClose = result;
    return { duration, result };
  }
  async closeMini() {
    await this.killProcessTree(this.closeProcess);
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('close.exe'),
      key: 'close', // 添加进程标识
    });
    this.closeProcess = result;
    return { duration, result };
  }
  async closeMitmBrowser() {
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('close_mitm_browser.exe'),
      key: 'close_mitm_browser', // 添加进程标识
    });
    return { duration, result };
  }
  // 抽奖自动重启
  lotterySignAutoByTime(options: {
    startTime: Date;
    endTime: Date;
    resetNum?: number;
  }) {
    const { resetNum = 50 } = options;
    let count = 0;
    const run = async () => {
      if (count % resetNum === 0) {
        await this.runReset();
      }
      count++;
      await this.lotterySign();
    };
    return executeContinuously(run, {
      ...options,
    });
  }
  // pdf自动签到
  async pdfLotterSignAutoByTime(options: {
    startTime: Date;
    endTime: Date;
    resetNum?: number;
  }) {
    const { resetNum = 100 } = options;
    let count = 0;
    const run = async () => {
      if (count % resetNum === 0) {
        await this.restartMitm();
      }
      count++;
      await this.pdfLotterSign();
      await this.lotterySignSwitch();
    };
    return executeContinuously(run, {
      ...options,
    });
  }
  async lotterySignSwitch() {
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('lottery_sign_switch.exe'),
      key: 'lottery_sign_switch', // 添加进程标识
    });
    return { duration, result };
  }
  async lotterySignReload() {
    const { duration, result } = await this.run({
      commandStr: this.getScriptPath('lottery_sign_reload.exe'),
      key: 'lottery_sign_reload', // 添加进程标识
    });
    return { duration, result };
  }
  async lotterySignFast() {
    await this.lotterySignReload();
    const startTime = new Date();
    await this.lotterySignSwitch();
    await this.lotterySignSwitch();
    await this.lotterySignSwitch();
    await this.lotterySignSwitch();
    await this.lotterySignSwitch();
    await this.lotterySignSwitch();
    const endTime = new Date();
    await awaitTime(20500 - (endTime.getTime() - startTime.getTime()));
  }
  lotterySignFastLoop() {
    let count = 0;
    const run = async () => {
      if (count % 70 === 0) {
        await this.closeMini();
        await this.runReset();
        await this.lotterySignNotClose();
        await this.lotterySignNotClose();
      }
      count++;
      await this.lotterySignFast();
      void run();
    };
    void run();
  }
  lotterySignFastByTime(options: {
    startTime: Date;
    endTime: Date;
    resetNum?: number;
  }) {
    const { resetNum = 70 } = options;
    let count = 0;
    const run = async () => {
      if (count % resetNum === 0) {
        await this.closeMini();
        await this.runReset();
        await this.lotterySignNotClose();
        await this.lotterySignNotClose();
      }
      count++;
      await this.lotterySignFast();
    };
    return executeContinuously(run, {
      ...options,
    });
  }
  // 抽奖自动重启循环
  lotterySignAutoLoop(options?: { resetNum?: number }) {
    const { resetNum = 50 } = options || {};
    let count = 0;
    const run = async () => {
      if (count % resetNum === 0) {
        await this.runReset();
      }
      count++;
      await this.lotterySign();
      void run();
    };
    void run();
  }
  // 设置进程退出处理
  private setupExitHandlers(): void {
    const exitHandler = async (signal: string) => {
      this.logger.log(`接收到 ${signal} 信号，终止进程...`);
      await this.cleanup();
      process.exit(0);
    };

    process.on('exit', () => void exitHandler('exit'));
    process.on('SIGINT', () => void exitHandler('SIGINT'));
    process.on('SIGTERM', () => void exitHandler('SIGTERM'));
    // eslint-disable-next-line @typescript-eslint/no-misused-promises
    process.on('uncaughtException', async (err) => {
      this.logger.error('未捕获异常', err.stack);
      await this.cleanup();
      process.exit(1);
    });
  }

  // 清理资源
  private async cleanup(): Promise<void> {
    this.logger.log('终止所有子进程...');

    // 终止所有注册的进程
    for (const [key, process] of processes) {
      this.logger.log(`终止进程: ${key}`);
      await this.killProcessTree(process);
    }

    // 确保 mitmProcess 也被终止
    if (mitmProcess) {
      await this.killProcessTree(mitmProcess);
    }

    processes.clear();
    mitmProcess = null;

    this.logger.log('所有子进程已终止');
  }

  // 改进的终止进程方法
  private async killProcessTree(process: ChildProcess | null): Promise<void> {
    if (!process || !process.pid) return;

    this.logger.log(`终止进程 PID: ${process.pid}`);

    return new Promise((resolve) => {
      // Windows 和 Unix 使用不同的终止命令
      const isWindows = platform() === 'win32';
      const isMac = platform() === 'darwin';
      // const isLinux = platform() === 'linux';
      let command = '';
      if (isWindows) {
        command = `taskkill /F /T /PID ${process.pid}`;
      } else if (isMac) {
        command = `kill -9 ${process.pid}`;
      } else {
        command = `pkill -P ${process.pid}`;
      }

      exec(command, (error) => {
        if (error) {
          this.logger.error(`终止进程失败: ${error.message}`);
        } else {
          this.logger.log(`进程 ${process.pid} 已终止`);
        }
        resolve();
      });
    });
  }
}
